package com.uziot.bucket.common.util;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.*;

/**
 * @author shidt
 * @version V1.0
 * @className ReflectionUtils
 * @date 2020-12-30 22:43:30
 * @description 增强型反射工具类
 */
public final class ReflectionUtils {
    private ReflectionUtils() {
    }

    public static String getPropertyParams(Object[] params, int[] indexes, String[] fieldNames) throws Exception {
        String lockParams = "";
        // 2个及2个以上参数时，fieldNames数量应与indexes一致
        if (fieldNames.length > 1 && indexes.length != fieldNames.length) {
            throw new Exception("indexes与fieldNames数量不一致");
        }
        // 数组为空代默认返回空列表
        if (indexes.length > 0) {
            StringBuilder lockParamsBuffer = new StringBuilder();
            for (int i = 0; i < indexes.length; i++) {
                if (fieldNames.length == 0 || fieldNames[i] == null || fieldNames[i].length() == 0) {
                    lockParamsBuffer.append(".").append(params[indexes[i]]);
                } else {
                    Object lockParamValue = getFieldVal(params[indexes[i]], fieldNames[i], true);
                    lockParamsBuffer.append(".").append(lockParamValue);
                }
            }
            lockParams = lockParamsBuffer.toString();
            // 去除开始
            if (lockParams.startsWith(".")) {
                lockParams = lockParams.substring(1);
            }
        }
        return lockParams;
    }

    /**
     * 获取字段名对应的字段值
     *
     * @param cls            class
     * @param obj            对象
     * @param fieldName      字段值
     * @param throwException 是否抛出异常
     * @return 对象赋值
     * @throws IllegalArgumentException IllegalArgumentException
     */
    private static Object getFieldVal(Class<?> cls, Object obj, String fieldName, boolean throwException)
            throws IllegalArgumentException {
        Field field = null;
        Class<?> currClass = cls;

        while (currClass != null) {
            try {
                field = currClass.getDeclaredField(fieldName);
                break;
            } catch (SecurityException | ReflectiveOperationException var9) {
                currClass = currClass.getSuperclass();
            }
        }

        if (field == null) {
            if (throwException) {
                throw new IllegalArgumentException(
                        (obj == null ? "Static field " : "Field ") + "\"" + fieldName + "\" not found or not accessible");
            }
        } else {
            try {
                field.setAccessible(true);
            } catch (RuntimeException ignored) {
            }
            try {
                return field.get(obj);
            } catch (IllegalAccessException var8) {
                if (throwException) {
                    throw new IllegalArgumentException(
                            "Can't read " + (obj == null ? "static " : "") + " field \"" + fieldName + "\": " + var8);
                }
            }
        }

        return null;
    }

    /**
     * 获取字段值
     *
     * @param obj            对象
     * @param fieldName      字段名
     * @param throwException 是否抛出异常
     * @return 值
     * @throws IllegalArgumentException IllegalArgumentException
     */
    public static Object getFieldVal(Object obj, String fieldName, boolean throwException)
            throws IllegalArgumentException {
        if (obj != null && fieldName != null) {
            return getFieldVal(obj.getClass(), obj, fieldName, throwException);
        } else if (throwException) {
            throw new NullPointerException();
        } else {
            return null;
        }
    }

    /**
     * 获取静态字段值
     *
     * @param cls            类
     * @param fieldName      字段名
     * @param throwException 是否抛出异常
     * @return 结果
     * @throws IllegalArgumentException IllegalArgumentException
     */
    public static Object getStaticFieldVal(Class<?> cls, String fieldName, boolean throwException)
            throws IllegalArgumentException {
        if (cls != null && fieldName != null) {
            return getFieldVal(cls, null, fieldName, throwException);
        } else if (throwException) {
            throw new NullPointerException();
        } else {
            return null;
        }
    }

    /**
     * 获取翻转参数类型列表
     *
     * @param cls cls
     * @return List
     */
    private static List<Class<?>> getReverseMethodAttemptOrder(Class<?> cls) {
        List<Class<?>> reverseAttemptOrder = new ArrayList<>();

        for (Class<?> c = cls; c != null && c != Object.class; c = c.getSuperclass()) {
            reverseAttemptOrder.add(c);
        }

        Set<Class<?>> addedIfaces = new HashSet<>();
        LinkedList<Class<?>> ifaceQueue = new LinkedList<>();

        Class<?> c;
        Class<?>[] superIfaces;
        int var7;
        for (c = cls; c != null; c = c.getSuperclass()) {
            if (c.isInterface() && addedIfaces.add(c)) {
                ifaceQueue.add(c);
            }

            superIfaces = c.getInterfaces();
            int var6 = superIfaces.length;

            for (var7 = 0; var7 < var6; ++var7) {
                Class<?> iface = superIfaces[var7];
                if (addedIfaces.add(iface)) {
                    ifaceQueue.add(iface);
                }
            }
        }

        while (true) {
            do {
                if (ifaceQueue.isEmpty()) {
                    return reverseAttemptOrder;
                }

                c = ifaceQueue.remove();
                reverseAttemptOrder.add(c);
                superIfaces = c.getInterfaces();
            } while (superIfaces.length <= 0);

            Class<?>[] var11 = superIfaces;
            var7 = superIfaces.length;

            for (int var12 = 0; var12 < var7; ++var12) {
                Class<?> superIface = var11[var12];
                if (addedIfaces.add(superIface)) {
                    ifaceQueue.add(superIface);
                }
            }
        }
    }

    /**
     * invokeMethod
     *
     * @param cls            cls
     * @param obj            obj
     * @param methodName     methodName
     * @param oneArg         oneArg
     * @param argType        argType
     * @param param          param
     * @param throwException throwException
     * @return Object
     * @throws IllegalArgumentException IllegalArgumentException
     */
    private static Object invokeMethod(Class<?> cls, Object obj, String methodName,
                                       boolean oneArg, Class<?> argType, Object param,
                                       boolean throwException) throws IllegalArgumentException {
        Method method = null;
        List<Class<?>> reverseAttemptOrder = getReverseMethodAttemptOrder(cls);
        int i = reverseAttemptOrder.size() - 1;

        while (i >= 0) {
            Class<?> classOrInterface = reverseAttemptOrder.get(i);

            try {
                method = oneArg ? classOrInterface.getDeclaredMethod(methodName, argType) : classOrInterface.getDeclaredMethod(methodName);
                break;
            } catch (SecurityException | ReflectiveOperationException var15) {
                --i;
            }
        }

        if (method == null) {
            if (throwException) {
                throw new IllegalArgumentException((obj == null ? "Static method " : "Method ") + "\"" + methodName + "\" not found or not accesible");
            }
        } else {
            try {
                method.setAccessible(true);
            } catch (RuntimeException ignored) {
            }

            try {
                return oneArg ? method.invoke(obj, param) : method.invoke(obj);
            } catch (SecurityException | IllegalAccessException var13) {
                if (throwException) {
                    throw new IllegalArgumentException("Can't call " + (obj == null ? "static " : "") + "method \"" + methodName + "\": " + var13);
                }
            } catch (InvocationTargetException var14) {
                if (throwException) {
                    throw new IllegalArgumentException("Exception while invoking " + (obj == null ? "static " : "") + "method \"" + methodName + "\"", var14);
                }
            }
        }

        return null;
    }

    /**
     * 实现方法
     *
     * @param obj            obj
     * @param methodName     methodName
     * @param throwException throwException
     * @return Object
     * @throws IllegalArgumentException IllegalArgumentException
     */
    public static Object invokeMethod(Object obj, String methodName, boolean throwException)
            throws IllegalArgumentException {
        if (obj != null && methodName != null) {
            return invokeMethod(
                    obj.getClass(), obj, methodName, false, null, null, throwException);
        } else if (throwException) {
            throw new NullPointerException();
        } else {
            return null;
        }
    }

    /**
     * 实现方法
     *
     * @param obj            对象
     * @param methodName     方法名
     * @param argType        参数类型
     * @param param          参数列表
     * @param throwException throwException
     * @return Object
     * @throws IllegalArgumentException IllegalArgumentException
     */
    public static Object invokeMethod(Object obj, String methodName, Class<?> argType, Object param, boolean throwException)
            throws IllegalArgumentException {
        if (obj != null && methodName != null) {
            return invokeMethod(obj.getClass(), obj, methodName, true, argType, param, throwException);
        } else if (throwException) {
            throw new NullPointerException();
        } else {
            return null;
        }
    }

    /**
     * 实现静态方法
     *
     * @param cls            cls
     * @param methodName     methodName
     * @param throwException throwException
     * @return Object
     * @throws IllegalArgumentException IllegalArgumentException
     */
    public static Object invokeStaticMethod(Class<?> cls, String methodName, boolean throwException)
            throws IllegalArgumentException {
        if (cls != null && methodName != null) {
            return invokeMethod(cls, null, methodName, false, null, null, throwException);
        } else if (throwException) {
            throw new NullPointerException();
        } else {
            return null;
        }
    }

    /**
     * 实现静态方法
     *
     * @param cls            cls
     * @param methodName     methodName
     * @param argType        argType
     * @param param          param
     * @param throwException throwException
     * @return Object
     * @throws IllegalArgumentException IllegalArgumentException
     */
    public static Object invokeStaticMethod(Class<?> cls, String methodName, Class<?> argType, Object param, boolean throwException) throws IllegalArgumentException {
        if (cls != null && methodName != null) {
            return invokeMethod(cls, (Object) null, methodName, true, argType, param, throwException);
        } else if (throwException) {
            throw new NullPointerException();
        } else {
            return null;
        }
    }

    /**
     * 通过类名获取class
     *
     * @param className className
     * @return Class
     */
    public static Class<?> classForNameOrNull(String className) {
        try {
            return Class.forName(className);
        } catch (LinkageError | ReflectiveOperationException var2) {
            return null;
        }
    }
}